package device

import (
	"bytes"
	"encoding/binary"
	"errors"
	"fmt"
	"reflect"

	"pac/packet"
)

// Processor 处理器
type Processor interface {
	// 处理函数
	Process(*Client) error
}

type Packet interface {
	packet.Packet

	Processor

	// 包数据合并自其它包
	mergeFrom(pkt Packet)

	// 解析参数
	parseParam() error

	// 保存解析参数时出现的错误
	setErrOfParseParam(err error)
	// 取出解析子类包时出的错误
	GetErrOfParseParam() error

	// 修正包 Cmd
	adjustCmd()
	// 编码参数
	marshalParams() ([]byte, error)
	// 编码头部
	marshalHead() ([]byte, error)
	// 完成编码
	marshalFinal(head, payload []byte) ([]byte, error)
}

// Marshal 将包编码为传输数据
func Marshal(pkt Packet) ([]byte, error) {
	if pkt == nil {
		return nil, errors.New("device.Marshal: packet is nil")
	}
	pkt.adjustCmd()

	payload, err := pkt.marshalParams()
	if err != nil {
		return nil, err
	}

	head, err := pkt.marshalHead()
	if err != nil {
		return nil, err
	}

	return pkt.marshalFinal(head, payload)
}

type devPkt struct {
	// 命令
	Cmd Cmd

	// 数据
	Payload []byte

	// 原始数据
	Raw []byte

	ErrOfParseParam error
}

func (p *devPkt) Bytes() int {
	if p == nil {
		return 0
	}
	return len(p.Raw)
}

func (p *devPkt) GetCmd() packet.Cmd {
	return &p.Cmd
}

func (p *devPkt) CmdStr() string {
	return p.GetCmd().String()
}

func (p *devPkt) IsRequest() bool {
	return (p.Cmd & 0x80) == 0
}

func (p *devPkt) IsResponse() bool {
	return (p.Cmd & 0x80) != 0
}

func (p *devPkt) GetPayload() []byte {
	return p.Payload
}

func (p *devPkt) GetRaw() []byte {
	return p.Raw
}

func (p *devPkt) String() string {
	if p == nil {
		return ""
	}
	return fmt.Sprintf("%s", p.CmdStr())
}

func (p *devPkt) mergeFrom(src Packet) {
	p.Cmd = *src.GetCmd().(*Cmd)
	p.Raw = src.GetRaw()
	p.Payload = src.GetPayload()
	p.ErrOfParseParam = src.GetErrOfParseParam()
}

func (p *devPkt) parseParam() error {
	if p == nil {
		return errors.New("devPkt.parseParam: packet is nil")
	}
	return errors.New("devPkt.parseParam: not implemented")
}

func (p *devPkt) setErrOfParseParam(err error) {
	if p != nil {
		p.ErrOfParseParam = err
	}
}

// GetErrOfParseParam 解析包参数时出的错误
func (p *devPkt) GetErrOfParseParam() error {
	if p == nil {
		return nil
	}
	return p.ErrOfParseParam
}

func (p *devPkt) adjustCmd() {
}

func (p *devPkt) marshalHead() ([]byte, error) {
	if p == nil {
		return nil, errors.New("devPkt.marshalHead: packet is nil")
	}
	return []byte{byte(p.Cmd), 0, 0}, nil
}

func (p *devPkt) marshalFinal(head, payload []byte) ([]byte, error) {
	if p == nil {
		return nil, errors.New("devPkt.marshalFinal: packet is nil")
	}
	length := len(head)
	lenPayload := len(payload)
	if lenPayload > 0 {
		length += lenPayload
	}
	raw := head
	lenPayload &= 0xFFFF
	binary.LittleEndian.PutUint16(raw[1:], uint16(lenPayload))
	if lenPayload > 0 {
		payloadIdx := len(raw)
		raw = append(raw, payload...)
		p.Payload = raw[payloadIdx:]
	}
	raw = append(raw, Checksum(raw))
	p.Raw = raw
	return raw, nil
}

func (p *devPkt) Process(cli *Client) error {
	return errors.New("devPkt.Process: not implemented")
}

func (p *devPkt) marshalParams() ([]byte, error) {
	if p == nil {
		return nil, errors.New("devPkt.marshalParams: packet is nil")
	}
	return nil, errors.New("devPkt.marshalParams: not implemented")
}

func (p *devPkt) Marshal() ([]byte, error) {
	if p == nil {
		return nil, errors.New("devPkt.Marshal: packet is nil")
	}
	return nil, errors.New("devPkt.Marshal: not implemented")
}

// reqPkt 请求包
type reqPkt struct {
	devPkt
}

func (p *reqPkt) String() string {
	if p == nil {
		return ""
	}
	return p.devPkt.String()
}

func (p *reqPkt) marshalHead() ([]byte, error) {
	if p == nil {
		return nil, errors.New("reqPkt.marshalHead: packet is nil")
	}
	return p.devPkt.marshalHead()
}

func (p *reqPkt) marshalParams() ([]byte, error) {
	if p == nil {
		return nil, errors.New("reqPkt.marshalParams: packet is nil")
	}
	return nil, nil
}

func (p *reqPkt) Marshal() ([]byte, error) {
	if p == nil {
		return nil, errors.New("reqPkt.Marshal: packet is nil")
	}
	return Marshal(p)
}

// reqPkt 回响包
type rspPkt struct {
	devPkt
	ErrCode
}

func (p *rspPkt) String() string {
	if p == nil {
		return ""
	}
	return p.devPkt.String() + " " + p.ErrCode.String()
}

func (p *rspPkt) marshalParams() ([]byte, error) {
	if p == nil {
		return nil, errors.New("rspPkt.marshalParams: packet is nil")
	}
	return []byte{byte(p.ErrCode & 0xFF)}, nil
}

func (p *rspPkt) marshalHead() ([]byte, error) {
	if p == nil {
		return nil, errors.New("rspPkt.marshalHead: packet is nil")
	}
	return p.devPkt.marshalHead()
}

func (p *rspPkt) Marshal() ([]byte, error) {
	if p == nil {
		return nil, errors.New("rspPkt.Marshal: packet is nil")
	}
	return Marshal(p)
}

func (p *rspPkt) mergeFrom(src Packet) {
	p.devPkt.mergeFrom(src)
	s, ok := src.(*rspPkt)
	if ok {
		p.ErrCode = s.ErrCode
	}
}

func (p *rspPkt) fromReq(src Packet) {
	if p != nil && (*src.GetCmd().(*Cmd)&0x80) == 0 {
		p.Cmd = *src.GetCmd().(*Cmd) | 0x80
	}
}

func (p *rspPkt) getErrCode() ErrCode {
	if p == nil {
		return 0
	}
	return p.ErrCode
}

func decode(dat []byte) (pkt *devPkt, err error) {
	datLen := len(dat)
	if datLen >= 3 { // CMD PARAM_LEN = 1 + 2 = 3
		paramLen := int(binary.LittleEndian.Uint16(dat[1:]))
		if paramLen > 1024+68 {
			return nil, fmt.Errorf("param len too big")
		}
		// CMD PARAM_LEN PARAM CHECKSUM = 1 + 2 + n + 1
		pktLen := 1 + 2 + paramLen + 1
		if datLen >= pktLen {
			tmp := dat[:pktLen]
			if Checksum(tmp) == 0 {

				raw := make([]byte, pktLen)
				copy(raw, tmp)

				return &devPkt{
					Raw:     raw,
					Cmd:     Cmd(raw[0]),
					Payload: raw[3 : pktLen-1],
				}, nil
			}
			// 校验错误
			return nil, errors.New("BCC error")
		}
	}

	return nil, nil
}

func DePacket(dat *bytes.Buffer) (pkt Packet, err error) {
	basePkt, err := decode(dat.Bytes())
	if err != nil {
		return nil, err
	}

	dat.Next(basePkt.Bytes())

	if basePkt == nil {
		return nil, nil
	}

	pkt = parseParam(basePkt)

	return pkt, nil
}

var cmdPktMap = make(map[string]reflect.Type)

func parseParam(pkt Packet) Packet {
	template := cmdPktMap[pkt.CmdStr()]
	if template == nil {
		pkt.setErrOfParseParam(fmt.Errorf("packet: parse param: unexpected cmd %v", pkt.CmdStr()))
	} else {
		newPkt := reflect.New(template.Elem()).Interface().(Packet)
		newPkt.mergeFrom(pkt)
		err := newPkt.parseParam()
		if err != nil {
			newPkt.setErrOfParseParam(err)
		}
		pkt = newPkt
	}
	return pkt
}
